#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include "sock.h"
#include "sockdist.h"
#include <cstdint>
#include <unistd.h>
#include <errno.h>
#include <string.h>

/* Fichier client */

using namespace std;

struct Interval
{
  int64_t a;
  int64_t b;
};

struct Interval * Icurrent;
struct Interval * Idone;
int descDist;
pthread_mutex_t lock_current_i = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock_done_i = PTHREAD_MUTEX_INITIALIZER;
bool closeBR=false;
pthread_mutex_t lock_closeBR = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t updated_i;

void * threadEmission(void * param);

void * threadReception(void * param)
{
  cout<<"# Activation du thread de réception ("<<pthread_self()<<")"<<endl;

  updated_i = PTHREAD_COND_INITIALIZER;
  
  int Rrecv;
  
  Icurrent->a=0; Icurrent->b=0; //Initialisation pour rentrer dans la boucle une première fois
  Idone->a=-1; Idone->b=-1; //De façon à attendre dans le thread d'émission

  pthread_mutex_lock(&lock_current_i);
  /* Création du thread d'émission */
  pthread_t tSend;
  if (pthread_create(&tSend,NULL,threadEmission,NULL) != 0)
  {
    cerr<<"Erreur lors de la création du thread d'émission"<<endl;
    exit(4);
  }
  /*******/

  /* Soit j'ai déjà le verrou, soit je le récupère */
  while (((pthread_mutex_trylock(&lock_current_i)) || (not pthread_mutex_lock(&lock_current_i))) && ((Icurrent->a>=0) && (Icurrent->b>=0)))
  {
    /* Réception */
    Rrecv = recv(descDist,Icurrent,sizeof(struct Interval),0);
    if (Rrecv<0)
    {
      cerr<<"Erreur lors de la réception"<<endl;
      break;
      //pthread_exit(NULL);
    }
    if (Rrecv==0)
    {
      cerr<<"Connexion interrompue"<<endl;
      break;
      //pthread_exit(NULL);
    }
    cout<<"Intervalle : ("<<Icurrent->a<<","<<Icurrent->b<<")"<<endl;
	  
    if ((Icurrent->a>=0) && (Icurrent->b>=0))
    {
      pthread_mutex_unlock(&lock_current_i);    
      sleep(3); //Simulation de traitement
      pthread_mutex_lock(&lock_current_i);
    }

    /*      * *      */
    pthread_mutex_lock(&lock_done_i);
    Idone->a = Icurrent->a;
    Idone->b = Icurrent->b;
    pthread_cond_broadcast(&updated_i); //On fait savoir que l'intervalle est mis à jour
    pthread_mutex_unlock(&lock_done_i);
    pthread_mutex_unlock(&lock_current_i);
    
  }
  //Ici on a l'intervalle (-1,-1), donc on ferme
  cout<<"# Arrêt du thread de réception"<<endl;
  Icurrent->a=-1; Icurrent->b=-1;
  pthread_cond_broadcast(&updated_i);
  pthread_mutex_unlock(&lock_current_i);
  pthread_join(tSend,NULL);
  pthread_cond_destroy(&updated_i);
  pthread_exit(NULL);
}

void * threadEmission(void * param)
{
  cout<<"# Activation du thread d'émission  ("<<pthread_self()<<")"<<endl;

  int Rsend;
  while ((not pthread_mutex_lock(&lock_current_i)) && (Icurrent->a>=0) && (Icurrent->b>=0))
  {
    cout<<Icurrent->a<<"  "<<Icurrent->b<<endl;
    cout<<"inzeboukle"<<endl;
    pthread_mutex_unlock(&lock_current_i);

    pthread_mutex_lock(&lock_done_i);
    cout<<"attente..."<<endl;
    pthread_cond_wait(&updated_i,&lock_done_i);
    
    cout<<"Envoi de l'intervalle"<<endl;
    Rsend = send(descDist,Idone,sizeof(struct Interval),0);
    pthread_mutex_unlock(&lock_done_i);
    
    if (Rsend<0)
    {
      cerr<<"Erreur lors de l'envoi du message"<<endl;
      pthread_exit(NULL);
    }
    if (Rsend==0)
    {
      cerr<<"Connexion avec le serveur interrompue"<<endl;
      pthread_exit(NULL);
    }
  }
  cout<<"# Arrêt du thread d'émission"<<endl;
  pthread_exit(NULL);
}
  
int main(int argc, char *argv[], char *envp[]) {
  Icurrent = new struct Interval;
  Idone = new struct Interval;
  
  if(argc != 2)
  {
    cout << "Format : "<<argv[0]<<" {adresse serveur}" << endl;
    return 1;
  }

  cout<<"# Création de la boîte réseau locale"<<endl;
  Sock BRLoc(SOCK_STREAM,0);
  if (BRLoc.good())
  {
    descDist=BRLoc.getsDesc();
    cout<<"# Boîte réseau créée"<<endl;
  }
  else
  {
    cerr<<"# Erreur lors de la création de la boîte réseau"<<endl;
    exit(1);
  }

  /*Désignation de la BR distante*/
  SockDist BRPub(argv[1],short(3456));
  struct sockaddr_in * adrBRPub = BRPub.getAdrDist();
  socklen_t lenBRPub = sizeof(struct sockaddr_in);

  cout<<"# Connexion au serveur..."<<endl;

  int Rconnect = connect(descDist,(struct sockaddr *)adrBRPub,lenBRPub);

  if (Rconnect<0)
  {
    cerr<<"Erreur lors de la demande de connexion ("<<Rconnect<<")"<<endl;
    perror("Détails ");
    exit(2);
  }
  cout<<"# Connexion réussie"<<endl;

  pthread_t tRecv;

  if (pthread_create(&tRecv,NULL,threadReception,NULL) != 0)
  {
    cerr<<"Erreur lors de la création du thread de réception"<<endl;
    exit(3);
  }
  
  pthread_join(tRecv,NULL);

  cout<<"# Fermeture de la boîte réseau locale"<<endl;
  shutdown(descDist,SHUT_RDWR);
  
  return EXIT_SUCCESS;
}
